﻿/*******************************************************************************
Copyright (c) 2011-2013, PKStudio

http://pkstudio.org

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Alpha Blending:
	http://en.wikipedia.org/wiki/Alpha_blending#Alpha_blending

HSL & HSV:
	http://en.wikipedia.org/wiki/HSL_and_HSV
*******************************************************************************/

using System;
using System.Diagnostics;
using System.Drawing;

namespace PKEngine
{
	/// <summary>
	/// Alpha BitBlt for RGB565.
	/// </summary>
	internal class PKAlphaForRgb565 : PKAlpha
	{
		#region Private Constant
		private const ushort ColorKeyRgb565 = 0xF81F;
		private const int MaxAlphaValue = 32;
		private const int MaxAlphaMove = 5;
		private const ushort AlphaMask = 0x0000;	// 0000000000000000
		private const ushort RedMask = 0xf800;		// rrrrr00000000000
		private const ushort GreenMask = 0x07e0;	// 00000gggggg00000
		private const ushort BlueMask = 0x001f;		// 00000000000bbbbb
		private const int AlphaMove = 16;
		private const int RedMove = 11;
		private const int GreenMove = 6;
		private const uint RgbMask = 0x07e0f81f; 	// 00000gggggg00000rrrrr000000bbbbb
		private const ushort HalfMask = 0x7bef;		// 0rrrr0ggggg0bbbb
		private const ushort CarryMask = 0x8410;	// r0000g00000b0000
		private const ushort LastOneMask = 0x0821;	// 0000100000100001
		private const ushort LastZeroMask = 0xf7de;	// 1111011111011110
		private ushort[] PixelAddTable;
		private ushort[] PixelSubTable;
		private ushort[] PixelGrayTable;
		private ushort[] RgbToHsvTable;
		private ushort[] HsvToRgbTable;
		#endregion

		#region Initialization
		internal PKAlphaForRgb565()
		{
			unchecked
			{
				// This table caches the results of PixelAdd function.
				PixelAddTable = new ushort[65536];
				for (int temp = 0; temp < 65536; temp++)
				{
					int result = temp;
					if ((result & RedMask) > (RedMask >> 1)) result |= RedMask;			// If the red value is overflow.
					if ((result & GreenMask) > (GreenMask >> 1)) result |= GreenMask;	// If the green value is overflow.
					if ((result & BlueMask) > (BlueMask >> 1)) result |= BlueMask;		// If the blue value is overflow.
					PixelAddTable[temp] = (ushort)((result << 1) | LastOneMask);		// Set the last bit to one.
				}

				// This table caches the results of PixelSub function.
				PixelSubTable = new ushort[65536];
				for (int temp = 0; temp < 65536; temp++)
				{
					int result = temp;
					if ((result & RedMask) <= (RedMask >> 1)) result &= GreenMask | BlueMask;	// If the red value is overflow.
					if ((result & GreenMask) <= (GreenMask >> 1)) result &= RedMask | BlueMask;	// If the red value is overflow.
					if ((result & BlueMask) <= (BlueMask >> 1)) result &= RedMask | GreenMask;	// If the red value is overflow.
					PixelSubTable[temp] = (ushort)((result << 1) & LastZeroMask);				// Set the last bit to zero.
				}
			}

			// This table caches the results of PixelGray function.
			PixelGrayTable = new ushort[65536];
			for (int temp = 0; temp < 65536; temp++)
			{
				// Gray = Red * 0.299 + Green * 0.587 + Blue * 0.114
				// Gray = (Red * 5 + Green * 9 + Blue * 2) / 16
				int result = (temp >> RedMove) * 5;
				result += ((temp & GreenMask) >> GreenMove) * 9;
				result += (temp & BlueMask) << 1;
				result >>= 4;

				PixelGrayTable[temp] = (ushort)((result << RedMove) | (result << GreenMove) | result);
			}

			// This table caches the results of converting RGB color to HSV color.
			RgbToHsvTable = new ushort[65536];
			for (int temp = 0; temp < 65536; temp++)
			{
				int red = temp >> RedMove;
				int green = (temp & GreenMask) >> GreenMove;
				int blue = temp & BlueMask;

				int max = Math.Max(red, Math.Max(green, blue));
				int min = Math.Min(red, Math.Min(green, blue));
				int chroma = max - min;

				int hue;
				if (chroma == 0)
				{
					hue = 0;
				}
				else if (red == max)
				{
					hue = (green - blue) * 5 / chroma;
				}
				else if (green == max)
				{
					hue = (blue - red) * 5 / chroma + 10;
				}
				else
				{
					hue = (red - green) * 5 / chroma + 20;
				}
				hue = (hue + 30) % 30;

				RgbToHsvTable[temp] = (ushort)((hue << RedMove) | (min << GreenMove) | max);
			}

			// This table caches the results of converting HSV color to RGB color.
			HsvToRgbTable = new ushort[65536];
			for (int temp = 0; temp < 65536; temp++)
			{
				int hue = temp >> RedMove;
				int min = (temp & GreenMask) >> GreenMove;
				int max = temp & BlueMask;
				int chroma = max - min;
				int red = 0, green = 0, blue = 0;
				switch (hue / 5)
				{
					case 0:
						// hue = (green - blue) * 5 / chroma
						// green = blue + hue * chroma / 5; 
						red = max;
						green = hue * chroma / 5 + min;
						blue = min;
						break;
					case 1:
						// hue = (blue - red) * 5 / chroma + 10
						// red = blue - (hue - 10) * chroma / 5
						// red = blue + (10 - hue) * chroma / 5
						red = min + (10 - hue) * chroma / 5;
						green = max;
						blue = min;
						break;
					case 2:
						// hue = (blue - red) * 5 / chroma + 10
						// blue = red + (hue - 10) * chroma / 5
						red = min;
						green = max;
						blue = min + (hue - 10) * chroma / 5;
						break;
					case 3:
						// hue = (red - green) * 5 / chroma + 20
						// green = red - (hue - 20) * chroma / 5
						// green = red + (20 - hue) * chroma / 5
						red = min;
						green = min + (20 - hue) * chroma / 5;
						blue = max;
						break;
					case 4:
						// hue = (red - green) * 5 / chroma + 20
						// red = green + (hue - 20) * chroma / 5
						red = min + (hue - 20) * chroma / 5;
						green = min;
						blue = max;
						break;
					case 5:
						// hue = (green - blue) * 5 / chroma + 30
						// blue = green - (hue - 30) * chroma / 5
						// blue = green + (30 - hue) * chroma / 5
						red = max;
						green = min;
						blue = min + (30 - hue) * chroma / 5;
						break;
				}
				HsvToRgbTable[temp] = (ushort)((red << RedMove) | (green << GreenMove) | blue);
			}
		}
		#endregion

		#region Public Properties
		public override ushort ColorKey
		{
			get
			{
				return ColorKeyRgb565;
			}
		}

		public override ushort ColorBlack
		{
			get
			{
				return AlphaMask;
			}
		}

		public override ushort ColorWhite
		{
			get
			{
				return 0x7FFF;
			}
		}

		public override ushort ColorRed
		{
			get
			{
				return RedMask;
			}
		}

		public override ushort ColorGreen
		{
			get
			{
				return GreenMask;
			}
		}

		public override ushort ColorBlue
		{
			get
			{
				return BlueMask;
			}
		}
		#endregion

		#region Public Methods
		public override ushort ColorFromRgb(int red, int green, int blue)
		{
			Debug.Assert(red >= 0 && red < 256, "PKAlpha.ColorFromRgba: Red value is invalid!");
			Debug.Assert(green >= 0 && green < 256, "PKAlpha.ColorFromRgba: Green value is invalid!");
			Debug.Assert(blue >= 0 && blue < 256, "PKAlpha.ColorFromRgba: Blue value is invalid!");
			red >>= 3;
			green >>= (3 - 1);
			blue >>= 3;
			return (ushort)((red << RedMove) | (green << (GreenMove - 1)) | blue);
		}

		public override ushort ColorFromRgba(int red, int green, int blue, int alpha)
		{
			Debug.Assert(alpha >= 0 && alpha < 256, "PKAlpha.ColorFromRgba: Alpha value is invalid!");
			return ColorFromRgb(red, green, blue);
		}

		public override int RedOfColor(ushort color)
		{
			return ((color & RedMask) >> RedMove) << 3;
		}

		public override int GreenOfColor(ushort color)
		{
			return ((color & GreenMask) >> GreenMove) << 2;
		}

		public override int BlueOfColor(ushort color)
		{
			return (color & BlueMask) << 3;
		}

		public override int AlphaOfColor(ushort color)
		{
			if (color == this.ColorKey)
			{
				return 0;
			}
			return 255;
		}

		public override void DrawLine(PKSurface dSurface, Point dPoint1, Point dPoint2, ushort color)
		{
			int xDistance = Math.Abs(dPoint1.X - dPoint2.X);
			int yDistance = Math.Abs(dPoint1.Y - dPoint2.Y);
			if (xDistance >= yDistance)
			{
				if (dPoint2.X < dPoint1.X)
				{
					Point temp = dPoint1;
					dPoint1 = dPoint2;
					dPoint2 = temp;
				}
				yDistance = dPoint2.Y - dPoint1.Y;

				int x, y;
				for (int i = 0; i <= xDistance; i++)
				{
					x = dPoint1.X + i;
					y = dPoint1.Y + yDistance * i / xDistance;
					dSurface.SetPixel(x, y, color);
				}
			}
			else
			{
				if (dPoint2.Y < dPoint1.Y)
				{
					Point temp = dPoint1;
					dPoint1 = dPoint2;
					dPoint2 = temp;
				}
				xDistance = dPoint2.X - dPoint1.X;

				int x, y;
				for (int i = 0; i <= yDistance; i++)
				{
					x = dPoint1.X + xDistance * i / yDistance;
					y = dPoint1.Y + i;
					dSurface.SetPixel(x, y, color);
				}
			}
		}

		public override void DrawRectangle(PKSurface dSurface, Rectangle dRect, ushort color)
		{
			Point p1, p2, p3, p4;
			p1 = new Point(dRect.Left, dRect.Top);
			p2 = new Point(dRect.Left, dRect.Bottom);
			p3 = new Point(dRect.Right, dRect.Bottom);
			p4 = new Point(dRect.Right, dRect.Top);
			this.DrawLine(dSurface, p1, p2, color);
			this.DrawLine(dSurface, p2, p3, color);
			this.DrawLine(dSurface, p3, p4, color);
			this.DrawLine(dSurface, p4, p1, color);
		}

		public override void Fill(PKSurface dSurface, Rectangle dRect, ushort color)
		{
			int width = dRect.Width;
			int height = dRect.Height;
			int dIndex = dSurface.ContentSize.Width * dRect.Y + dRect.X;
			int dSkip = dSurface.ContentSize.Width - dRect.Width;

			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					dSurface.Data[dIndex] = color;
					dIndex++;
				}
				dIndex += dSkip;
			}
		}

		public override void FillAlpha(PKSurface dSurface, Rectangle dRect, ushort color, int alpha)
		{
			if (alpha >= 256)
			{
				this.Fill(dSurface, dRect, color);
				return;
			}
			else
			{
				alpha >>= 3;
			}

			int width = dRect.Width;
			int height = dRect.Height;
			int dIndex = dSurface.ContentSize.Width * dRect.Y + dRect.X;
			int dSkip = dSurface.ContentSize.Width - dRect.Width;

			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					dSurface.Data[dIndex] = PixelAlpha_(dSurface.Data[dIndex], color, alpha);
					dIndex++;
				}
				dIndex += dSkip;
			}
		}

		public override void FillWithAlphaChannel(PKSurface dSurface, int dX, int dY, PKChannel sChannel, Rectangle sRect, ushort color)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sChannel.ContentSize.Width;
			int sSkip = sChannel.ContentSize.Width - width;
			int alpha;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					{
						alpha = sChannel.Data[sIndex];
						alpha = (alpha + 1) >> 3;
						dSurface.Data[dIndex] = PixelAlphaA(dSurface.Data[dIndex], color, alpha);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void Blt(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = sSurface.Data[sIndex];
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltStretch(PKSurface dSurface, Rectangle dRect, PKSurface sSurface, Rectangle sRect)
		{
			int dWidth = dRect.Width;
			int dHeight = dRect.Height;
			int dIndex = dRect.X + dRect.Y * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - dWidth;

			int[] sTable = new int[dWidth];
			for (int x = 0; x < dWidth; x++)
			{
				sTable[x] = x * sRect.Width / dWidth;
			}
			for (int x = 0; x < dWidth - 1; x++)
			{
				sTable[x] = sTable[x + 1] - sTable[x];
			}
			sTable[dWidth - 1] = 0;

			// Nearest-neighbor interpolation.
			for (int y = 0; y < dHeight; y++)
			{
				int sIndex = sRect.X + (sRect.Y + y * sRect.Height / dHeight) * sSurface.ContentSize.Width;
				for (int x = 0; x < dWidth; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = sSurface.Data[sIndex];
					}
					dIndex++;
					sIndex += sTable[x];
				}
				dIndex += dSkip;
			}
		}

		public override void BltAlpha(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect, int alpha)
		{
			if (alpha >= 256)
			{
				this.Blt(dSurface, dX, dY, sSurface, sRect);
				return;
			}
			else
			{
				alpha >>= 3;
			}

			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = PixelAlpha_(dSurface.Data[dIndex], sSurface.Data[sIndex], alpha);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltAlphaStretch(PKSurface dSurface, Rectangle dRect, PKSurface sSurface, Rectangle sRect, int alpha)
		{
			if (alpha >= 256)
			{
				this.BltStretch(dSurface, dRect, sSurface, sRect);
				return;
			}
			else
			{
				alpha >>= 3;
			}

			int dWidth = dRect.Width;
			int dHeight = dRect.Height;
			int dIndex = dRect.X + dRect.Y * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - dWidth;

			int[] sTable = new int[dWidth];
			for (int x = 0; x < dWidth; x++)
			{
				sTable[x] = x * sRect.Width / dWidth;
			}
			for (int x = 0; x < dWidth - 1; x++)
			{
				sTable[x] = sTable[x + 1] - sTable[x];
			}
			sTable[dWidth - 1] = 0;

			// Nearest-neighbor interpolation.
			for (int y = 0; y < dHeight; y++)
			{
				int sIndex = sRect.X + (sRect.Y + y * sRect.Height / dHeight) * sSurface.ContentSize.Width;
				for (int x = 0; x < dWidth; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = PixelAlpha_(dSurface.Data[dIndex], sSurface.Data[sIndex], alpha);
					}
					dIndex++;
					sIndex += sTable[x];
				}
				dIndex += dSkip;
			}
		}

		public override void BltAdd(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = PixelAdd(dSurface.Data[dIndex], sSurface.Data[sIndex]);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltSub(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = PixelSub(dSurface.Data[dIndex], sSurface.Data[sIndex]);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltGray(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = PixelGray(sSurface.Data[sIndex]);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltHue(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect, int hue)
		{
			hue /= 12;

			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = PixelHue(sSurface.Data[sIndex], hue);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltTint(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect, ushort color, int colorAlpha)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			if (colorAlpha >= 256)
			{
				for (int y = 0; y < height; y++)
				{
					for (int x = 0; x < width; x++)
					{
						if (sSurface.Data[sIndex] != ColorKey)
						{
							dSurface.Data[dIndex] = color;
						}
						dIndex++;
						sIndex++;
					}
					dIndex += dSkip;
					sIndex += sSkip;
				}
			}
			else
			{
				colorAlpha >>= 3;

				for (int y = 0; y < height; y++)
				{
					for (int x = 0; x < width; x++)
					{
						if (sSurface.Data[sIndex] != ColorKey)
						{
							dSurface.Data[dIndex] = PixelTint(sSurface.Data[sIndex], color, colorAlpha);
						}
						dIndex++;
						sIndex++;
					}
					dIndex += dSkip;
					sIndex += sSkip;
				}
			}
		}

		public override void BltColor(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect, ushort color)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						if (sSurface.Data[sIndex] != ColorBlack)
						{
							dSurface.Data[dIndex] = color;
						}
						else
						{
							dSurface.Data[dIndex] = ColorBlack;
						}
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltWithTransitionChannel(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect, PKChannel channel, int value)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (value > channel.Data[sIndex] && sSurface.Data[sIndex] != ColorKey)
					{
						dSurface.Data[dIndex] = sSurface.Data[sIndex];
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override void BltWithAlphaChannel(PKSurface dSurface, int dX, int dY, PKSurface sSurface, Rectangle sRect, PKChannel channel)
		{
			int width = sRect.Width;
			int height = sRect.Height;
			int dIndex = dX + dY * dSurface.ContentSize.Width;
			int dSkip = dSurface.ContentSize.Width - width;
			int sIndex = sRect.X + sRect.Y * sSurface.ContentSize.Width;
			int sSkip = sSurface.ContentSize.Width - width;
			int alpha;

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					if (sSurface.Data[sIndex] != ColorKey)
					{
						alpha = channel.Data[sIndex];
						alpha = (alpha + 1) >> 3;
						dSurface.Data[dIndex] = PixelAlphaA(dSurface.Data[dIndex], sSurface.Data[sIndex], alpha);
					}
					dIndex++;
					sIndex++;
				}
				dIndex += dSkip;
				sIndex += sSkip;
			}
		}

		public override ushort PixelAlpha(ushort dColor, ushort sColor, int alpha)
		{
			if (alpha >= 256)
			{
				alpha = MaxAlphaValue;
			}
			else
			{
				alpha >>= 3;
			}

			return PixelAlpha_(dColor, sColor, alpha);
		}
		#endregion

		#region Private Methods
		private static ushort PixelAlpha_(ushort dColor, ushort sColor, int alpha)
		{
			unchecked
			{
				uint dTemp = (uint)(((dColor << 16) | dColor) & RgbMask);
				uint sTemp = (uint)(((sColor << 16) | sColor) & RgbMask);
				// src * alpha + dst * (1 - alpha)			(alpha: 0 ~ 1.0)
				// (src * alpha + dst * (32 - alpha)) / 32	(alpha: 0 ~ 32)
				// ((src - dst) * alpha) / 32 + dst
				dTemp = (uint)(((((sTemp - dTemp) * alpha) >> MaxAlphaMove) + dTemp) & RgbMask);
				return (ushort)((dTemp >> 16) | dTemp);
			}
		}

		private static ushort PixelAlphaA(ushort dColor, ushort sColor, int alpha)
		{
			if (alpha == 0)
			{
				return dColor;
			}
			else if (alpha == MaxAlphaValue)
			{
				return sColor;
			}
			else
			{
				return PixelAlpha_(dColor, sColor, alpha);
			}
		}

		private ushort PixelAdd(ushort dColor, ushort sColor)
		{
			dColor = (ushort)((dColor >> 1) & HalfMask);
			sColor = (ushort)((sColor >> 1) & HalfMask);
			return PixelAddTable[dColor + sColor];
		}

		private ushort PixelSub(ushort dColor, ushort sColor)
		{
			dColor = (ushort)((dColor >> 1) & HalfMask | CarryMask);
			sColor = (ushort)((sColor >> 1) & HalfMask);
			return PixelSubTable[dColor - sColor];
		}

		private ushort PixelGray(ushort sColor)
		{
			return PixelGrayTable[sColor];
		}

		private ushort PixelHue(ushort sColor, int hue)
		{
			sColor = RgbToHsvTable[sColor];
			sColor = (ushort)(((sColor | RedMask) ^ RedMask) | (hue << RedMove));
			return HsvToRgbTable[sColor];
		}

		private static ushort PixelTint(ushort sColor, ushort color, int colorAlpha)
		{
			return PixelAlpha_(sColor, color, colorAlpha);
		}
		#endregion
	}
}
